<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>5. 自定义 input 表单控件 | 被删的前端游乐场</title>
    <meta name="description" content="Just playing around">
    
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.a683ed26.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.e5cf4092.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.c853767c.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.eed49fdd.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/57.7dfe2fbe.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.a683ed26.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端基础</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端基础</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-0cd75e06><span class="stand" data-v-0cd75e06></span> <div class="cat" data-v-0cd75e06><div class="body" data-v-0cd75e06></div> <div class="head" data-v-0cd75e06><div class="ear" data-v-0cd75e06></div> <div class="ear" data-v-0cd75e06></div></div> <div class="face" data-v-0cd75e06><div class="nose" data-v-0cd75e06></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div> <div class="whisker-container" data-v-0cd75e06><div class="whisker" data-v-0cd75e06></div> <div class="whisker" data-v-0cd75e06></div></div></div> <div class="tail-container" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06><div class="tail" data-v-0cd75e06></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>玩转 Angular</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/front-end-others/angular-free/0-about-angular-from-1-to-4.html" class="sidebar-link">谈谈 AngularJS 到 Angular</a></li><li><a href="/front-end-playground/front-end-others/angular-free/0-di-in-angular.html" class="sidebar-link">谈谈 Angular 的依赖注入</a></li><li><a href="/front-end-playground/front-end-others/angular-free/1-init-angular-app-with-webpack.html" class="sidebar-link">1. 用 Webpack 启动 Angular 应用</a></li><li><a href="/front-end-playground/front-end-others/angular-free/2-inprove-app-configuration.html" class="sidebar-link">2. 改善应用配置</a></li><li><a href="/front-end-playground/front-end-others/angular-free/3-init-routes-and-assets.html" class="sidebar-link">3. 启用路由和添加静态资源</a></li><li><a href="/front-end-playground/front-end-others/angular-free/4-create-sidebar.html" class="sidebar-link">4. 制作左侧自动定位菜单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html" class="active sidebar-link">5. 自定义 input 表单控件</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html#ngmodel-相关" class="sidebar-link">NgModel 相关</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html#时间选择控件" class="sidebar-link">时间选择控件</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/angular-free/5-custom-input-component.html#结束语" class="sidebar-link">结束语</a></li></ul></li><li><a href="/front-end-playground/front-end-others/angular-free/6-two-form-building.html" class="sidebar-link">6. 模型驱动和模板驱动的表单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/7-dynamic-form.html" class="sidebar-link">7. 创建动态表单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/8-radio-and-checkbox.html" class="sidebar-link">8. 表单的 radio 和 checkbox</a></li><li><a href="/front-end-playground/front-end-others/angular-free/9-upload-iamge.html" class="sidebar-link">9. 图片上传控件</a></li><li><a href="/front-end-playground/front-end-others/angular-free/10-add-conditions.html" class="sidebar-link">10. 向表单添加条件控制</a></li><li><a href="/front-end-playground/front-end-others/angular-free/11-option-dialog.html" class="sidebar-link">11. 使用动态表单制作选项配置对话框</a></li><li><a href="/front-end-playground/front-end-others/angular-free/12-config-and-create-form.html" class="sidebar-link">12. 配置以及生成表单</a></li><li><a href="/front-end-playground/front-end-others/angular-free/13-dynamic-list.html" class="sidebar-link">13. 动态列表配置</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Cycle.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>D3.js 和 Tree</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Three.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>BOX2D.js 文档与教程</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>该系列用于探索一些 Angular 中灵活或者新的用法。本文简单介绍封装使用 ngModol 实现自定义表单控件的过程。</p> <h2 id="ngmodel-相关"><a href="#ngmodel-相关" aria-hidden="true" class="header-anchor">#</a> NgModel 相关</h2> <h3 id="ngmodel"><a href="#ngmodel" aria-hidden="true" class="header-anchor">#</a> NgModel</h3> <p><code>NgModel</code>用于从作用域创建一个<code>FormControl</code>实例，并将它绑定到一个表单控制元素。</p> <ul><li><code>[ngModel]</code>: 单向绑定，作用域变更将同步到 UI 木板</li> <li><code>[(ngModel)]</code>: 双向绑定，UI 模版的变更也将同步到作用域</li></ul> <p><code>NgModel</code>继承自<code>NgControl</code>。</p> <h3 id="ngcontrol"><a href="#ngcontrol" aria-hidden="true" class="header-anchor">#</a> NgControl</h3> <p><code>NgControl</code>是所有控制指令继承的基础类。它将一个<code>FormControl</code>绑定到 DOM 元素。</p> <p><code>FormControl</code>、<code>FormGroup</code>和<code>FormArray</code>，三者都用于 angular 表单的值和状态的跟踪，区别在于是一个控件、一组控件或者是它们的组合。</p> <p><code>AbstractControl</code>是三个具体表单类的抽象基类。并为它们提供了一些共同的行为和属性，其中有些是可观察对象（<code>Observable</code>）。
<code>FormControl</code>用于跟踪一个单独的表单控件的值和有效性状态。它对应于一个 HTML 表单控件，比如输入框和下拉框。
<code>FormGroup</code>用于跟踪一组<code>AbstractControl</code>的实例的值和有效性状态。该组的属性中包含了它的子控件。组件中的顶级表单就是一个<code>FormGroup</code>。
<code>FormArray</code>用于跟踪<code>AbstractControl</code>实例组成的有序数组的值和有效性状态。</p> <h3 id="controlvalueaccessor"><a href="#controlvalueaccessor" aria-hidden="true" class="header-anchor">#</a> ControlValueAccessor</h3> <p><code>ControlValueAccessor</code>用于在控制和原生元素之间建立联系，它封装了赋值到一个表现为<code>input</code>元素的 DOM 元素。</p> <p>简单说，就是 angular 中的 input 是带有<code>[(ngModel)]</code>这个属性的，而我们想要自己控制这个 input 的写入过程，使用<code>ControlValueAccessor</code>就可以做到。</p> <p><code>ControlValueAccessor</code>提供以下接口：</p> <ul><li><code>writeValue(obj: any) : void</code>: 写入值到元素</li> <li><code>registerOnChange(fn: any) : void</code>: 设置当控件接收到<code>change</code>事件时触发的回调</li> <li><code>registerOnTouched(fn: any) : void</code>: 设置当控件接收到<code>touch</code>事件时触发的回调</li> <li><code>setDisabledState(isDisabled: boolean) : void</code>: 该函数将在控件状态或者<code>disabled</code>值变化，根据值来对元素启用或失效</li></ul> <p><code>ControlValueAccessor</code>继承自<code>DefaultValueAccessor</code>。</p> <h3 id="defaultvalueaccessor"><a href="#defaultvalueaccessor" aria-hidden="true" class="header-anchor">#</a> DefaultValueAccessor</h3> <p><code>DefaultValueAccessor</code>提供值写入和监听变化的默认访问，像<code>NgModel</code>, <code>FormControlDirective</code>, 和<code>FormControlName</code>指令会使用。</p> <p><code>DefaultValueAccessor</code>提供类包括：</p> <ul><li><code>onChange : (_: any) =&gt; {}</code>: <code>change</code>事件变化监听</li> <li><code>onTouched : () =&gt; {}</code>: <code>touch</code>事件变化监听</li></ul> <p>以及<code>ControlValueAccessor</code>（上面）的接口。</p> <h3 id="ng-value-accessor"><a href="#ng-value-accessor" aria-hidden="true" class="header-anchor">#</a> NG_VALUE_ACCESSOR</h3> <p><code>NG_VALUE_ACCESSOR</code>提供一个<code>ControlValueAccessor</code>供表单控制使用。</p> <h2 id="时间选择控件"><a href="#时间选择控件" aria-hidden="true" class="header-anchor">#</a> 时间选择控件</h2> <h3 id="datetimepicker"><a href="#datetimepicker" aria-hidden="true" class="header-anchor">#</a> datetimepicker</h3> <p>这里我们主要使用一个 Bootstrap 和 jQuery 的日期时间选择器插件--<a href="http://www.bootcss.com/p/bootstrap-datetimepicker/" target="_blank" rel="noopener noreferrer">bootstrap-datetimepick<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>。</p> <p>先简单介绍一下，我们可以使用该插件方便地进行日期和时间选择，从最大的十年视图到最小的分钟选择都可以自行调整。
具体一些配置项大家可以到官网上查看，这里就不详细介绍了，后面代码用到的会简单进行说明。</p> <p>首先我们需要下载代码，这里放在了<code>assets/plugins/datepicker</code>文件夹里面。</p> <p>然后添加进我们的应用程序就可以使用了：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// boostrap.ts</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./assets/plugins/datepicker/bootstrap-datetimepicker.min.js&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token string">&quot;./assets/plugins/datepicker/bootstrap-datetimepicker.zh-CN.js&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="创建自定义-input-控件"><a href="#创建自定义-input-控件" aria-hidden="true" class="header-anchor">#</a> 创建自定义 input 控件</h3> <p>我们想要封装后的组件跟原生的 angular 组件一样，像表现为 input 的自定义控件，我们想要使用<code>[(ngModel)]</code>来进行双向绑定，我们需要使用<code>ControlValueAccessor</code>来拓展。</p> <p>而这里<code>ControlValueAccessor</code>只是一个接口，我们应用它，还需要获取一些可用的服务，这时候需要注入<code>NG_VALUE_ACCESSOR</code>。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR</span><span class="token punctuation">:</span> any <span class="token operator">=</span> <span class="token punctuation">{</span>
  provide<span class="token punctuation">:</span> <span class="token constant">NG_VALUE_ACCESSOR</span><span class="token punctuation">,</span>
  useExisting<span class="token punctuation">:</span> <span class="token function">forwardRef</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> CustomInputComponent<span class="token punctuation">)</span><span class="token punctuation">,</span>
  multi<span class="token punctuation">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>这里简单讲讲几个概念：</p> <ol><li>我们自定义了一个访问控制服务，该服务包装为<code>NG_VALUE_ACCESSOR</code>服务，主要用于控制<code>ControlValueAccessor</code>相关的访问。</li> <li>我们需要将自定义 input 控件提供给<code>NG_VALUE_ACCESSOR</code>，以便通过自定义方式控制父组件的<code>[(ngModel)]</code>以及其他表单相关的访问。</li> <li><code>forwardRef</code>用于将目前还未获取到的依赖关联起来，这里我们关联后面的自定义 Input 组件。</li></ol> <div class="language-js extra-class"><pre class="language-js"><code>@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    selector<span class="token punctuation">:</span> <span class="token string">'custom-input'</span><span class="token punctuation">,</span>
    template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`&lt;input [(ngModel)]=&quot;value&quot; class=&quot;form-control&quot; (blur)=&quot;onBlur()&quot; /&gt;`</span></span><span class="token punctuation">,</span>
    providers<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token constant">CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR</span><span class="token punctuation">]</span> <span class="token comment">// 注入访问控制服务</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// 继承ControlValueAccessor接口</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">CustomInputComponent</span> <span class="token keyword">implements</span> <span class="token class-name">ControlValueAccessor</span><span class="token punctuation">{</span>

    <span class="token comment">// 内部model值</span>
    <span class="token keyword">private</span> innerValue<span class="token punctuation">:</span> any <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>

    <span class="token comment">// 定义ControlValueAccessor提供的事件回调</span>
    <span class="token keyword">private</span> <span class="token function-variable function">onTouched</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span> <span class="token operator">=</span> noop<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token function-variable function">onChange</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span> <span class="token operator">=</span> noop<span class="token punctuation">;</span>

    <span class="token comment">// 获取值的访问</span>
    <span class="token keyword">get</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> any <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>innerValue<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    <span class="token comment">// 设置值，同时触发change回调</span>
    <span class="token keyword">set</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token parameter">v<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>v <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>innerValue<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>innerValue <span class="token operator">=</span> v<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onChange</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 失焦时触发回调</span>
    <span class="token function">onBlur</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onTouched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 表单ControlValueAccessor接口</span>
    <span class="token function">writeValue</span><span class="token punctuation">(</span><span class="token parameter">value<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>innerValue<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>innerValue <span class="token operator">=</span> value<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 表单ControlValueAccessor接口</span>
    <span class="token function">registerOnChange</span><span class="token punctuation">(</span><span class="token parameter">fn<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>onChange <span class="token operator">=</span> fn<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 表单ControlValueAccessor接口</span>
    <span class="token function">registerOnTouched</span><span class="token punctuation">(</span><span class="token parameter">fn<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>onTouched <span class="token operator">=</span> fn<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>具体的实现实例参考<a href="https://embed.plnkr.co/nqKUSPWb6w5QXr8a0wEu/?show=preview" target="_blank" rel="noopener noreferrer">Angular2 + Connect custom component to ngModel<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a>。</p> <h3 id="创建自定义时间选择控件"><a href="#创建自定义时间选择控件" aria-hidden="true" class="header-anchor">#</a> 创建自定义时间选择控件</h3> <p>像我们定义一个时间选择控件，一般需要对外提供一些配置：</p> <ul><li>选择精度（或自定义视图范围）
<ul><li>这里提供：分钟（默认）、小时和天</li></ul></li> <li>可选日期范围</li> <li>是否禁用</li> <li>是否必填</li> <li>...</li></ul> <p>以及通常我们提供一个值变更的回调，像<code>(change)</code>这样的事件。
下面看看代码实现：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span>Component<span class="token punctuation">,</span> Input<span class="token punctuation">,</span> AfterViewInit<span class="token punctuation">,</span> ElementRef<span class="token punctuation">,</span> EventEmitter<span class="token punctuation">,</span> Output<span class="token punctuation">,</span> forwardRef<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>ControlValueAccessor<span class="token punctuation">,</span> <span class="token constant">NG_VALUE_ACCESSOR</span><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/forms'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR</span><span class="token punctuation">:</span> any <span class="token operator">=</span> <span class="token punctuation">{</span>
  provide<span class="token punctuation">:</span> <span class="token constant">NG_VALUE_ACCESSOR</span><span class="token punctuation">,</span>
  useExisting<span class="token punctuation">:</span> <span class="token function">forwardRef</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> DateTimePickerComponent<span class="token punctuation">)</span><span class="token punctuation">,</span>
  multi<span class="token punctuation">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'date-time-picker'</span><span class="token punctuation">,</span>
  template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`&lt;input type=&quot;text&quot; class=&quot;form-control&quot; [disabled]=&quot;disabled&quot; [(ngModel)]=&quot;value&quot; (blur)=&quot;onBlur()&quot; /&gt;`</span></span><span class="token punctuation">,</span>
  providers<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token constant">CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// ControlValueAccessor: A bridge between a control and a native element.</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">DateTimePickerComponent</span> <span class="token keyword">implements</span> <span class="token class-name">AfterViewInit</span><span class="token punctuation">,</span> ControlValueAccessor <span class="token punctuation">{</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> accuracy<span class="token punctuation">:</span> string<span class="token punctuation">;</span>  <span class="token comment">// 日期选择的精度，默认分钟，可选 (hour | day)</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> startDate<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 可选最早日期</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> endDate<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 可选最晚日期</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> maxView<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 最大视图</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> disabled<span class="token punctuation">:</span> boolean <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token comment">// 是否禁用</span>

  @<span class="token function">Output</span><span class="token punctuation">(</span><span class="token punctuation">)</span> change <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EventEmitter</span><span class="token operator">&lt;</span>any<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// input的change事件回调</span>

  <span class="token keyword">private</span> el<span class="token punctuation">;</span> <span class="token comment">// 控件元素</span>
  <span class="token keyword">private</span> model<span class="token punctuation">:</span> any<span class="token punctuation">;</span> <span class="token comment">// 内部model值</span>

  <span class="token comment">// 定义ControlValueAccessor提供的事件回调</span>
  <span class="token keyword">private</span> <span class="token function-variable function">onChange</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> <span class="token function-variable function">onTouched</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">:</span> ElementRef</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>el <span class="token operator">=</span> el<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Lifecycle hook that is called after a component's view has been fully initialized.</span>
  <span class="token function">ngAfterViewInit</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">/*
     source:http://www.bootcss.com/p/bootstrap-datetimepicker/
     minView: default 2
     maxView: default 4
     0 or 'hour' for the hour view （小时视图）
     1 or 'day' for the day view 0-24h （日视图）
     2 or 'month' for month view (the default) （月视图）
     3 or 'year' for the 12-month overview （年视图）
     4 or 'decade' for the 10-year overview. Useful for date-of-birth datetimepickers. （十年视图）
     */</span>
    <span class="token keyword">let</span> format <span class="token operator">=</span> <span class="token string">'yyyy-mm-dd hh:ii:00'</span><span class="token punctuation">;</span> <span class="token comment">// 默认选择分钟，故秒数为00，datetimepicker不支持秒数的选择</span>
    <span class="token keyword">let</span> minView <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 默认最小视图为分钟</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>accuracy <span class="token operator">===</span> <span class="token string">'hour'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      format <span class="token operator">=</span> <span class="token string">'yyyy-mm-dd hh:00'</span><span class="token punctuation">;</span> <span class="token comment">// 设置为小时，分钟和秒数需为00</span>
      minView <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 最小视图为小时</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>accuracy <span class="token operator">===</span> <span class="token string">'day'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      format <span class="token operator">=</span> <span class="token string">'yyyy-mm-dd 00:00:00'</span><span class="token punctuation">;</span> <span class="token comment">// 设置为天</span>
      minView <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// 最小视图为日期</span>
    <span class="token punctuation">}</span>

    <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>el<span class="token punctuation">.</span>nativeElement<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'input'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">datetimepicker</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      language<span class="token punctuation">:</span> <span class="token string">'zh-CN'</span><span class="token punctuation">,</span>
      autoclose<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 选择日期后自动关闭</span>
      maxView<span class="token punctuation">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>maxView<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token comment">// 选择的最大视图，4为十年视图</span>
      startDate<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>startDate <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token comment">// 最早可选日期，默认不限制</span>
      endDate<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>endDate <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token comment">// 最晚可选日期，默认不限制</span>
      format<span class="token punctuation">,</span> <span class="token comment">// 格式化</span>
      minView<span class="token punctuation">,</span> <span class="token comment">// 选择的最小视图</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'hide'</span><span class="token punctuation">,</span> <span class="token parameter">ev</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token comment">// 这里需要注意，我们使用箭头函数() =&gt; {}，则不会更改this的指向</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>ev<span class="token punctuation">.</span>target<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 更新值</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>change<span class="token punctuation">.</span><span class="token function">emit</span><span class="token punctuation">(</span><span class="token punctuation">{</span>value<span class="token punctuation">:</span> <span class="token function">$</span><span class="token punctuation">(</span>ev<span class="token punctuation">.</span>target<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 触发回调</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 获取值的访问</span>
  <span class="token keyword">get</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> any <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 设置值，同时触发change回调</span>
  <span class="token keyword">set</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token parameter">v<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>v <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>model <span class="token operator">=</span> v<span class="token punctuation">;</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onChange</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 失焦时触发回调</span>
  <span class="token function">onBlur</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onTouched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 父组件的值变更时，更新model的值</span>
  <span class="token function">writeValue</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> string<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span><span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>model <span class="token operator">=</span> value<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 表单ControlValueAccessor接口</span>
  <span class="token function">registerOnChange</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>onChange <span class="token operator">=</span> fn<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 表单ControlValueAccessor接口</span>
  <span class="token function">registerOnTouched</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>onTouched <span class="token operator">=</span> fn<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="抽象出-class-继承"><a href="#抽象出-class-继承" aria-hidden="true" class="header-anchor">#</a> 抽象出 class 继承</h3> <p>我们可以把相同的方法抽象出来，通过继承的方式，这样就能在多个相似组件通用了。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span>forwardRef<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>ControlValueAccessor<span class="token punctuation">,</span> <span class="token constant">NG_VALUE_ACCESSOR</span><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/forms'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">customInputAccessor</span><span class="token punctuation">(</span><span class="token parameter">component</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
        provide<span class="token punctuation">:</span> <span class="token constant">NG_VALUE_ACCESSOR</span><span class="token punctuation">,</span>
        useExisting<span class="token punctuation">:</span> <span class="token function">forwardRef</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> component<span class="token punctuation">)</span><span class="token punctuation">,</span>
        multi<span class="token punctuation">:</span> <span class="token boolean">true</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">CustomInputComponent</span> <span class="token keyword">implements</span> <span class="token class-name">ControlValueAccessor</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> model<span class="token punctuation">:</span> any<span class="token punctuation">;</span> <span class="token comment">// 控件的值</span>
    <span class="token keyword">private</span> <span class="token function-variable function">onChange</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token function-variable function">onTouched</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

    <span class="token comment">// get accessor</span>
    <span class="token keyword">get</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> any <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// set accessor including call the onchange callback</span>
    <span class="token keyword">set</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token parameter">v<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>v <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>model <span class="token operator">=</span> v<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onChange</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// Set touched on blur</span>
    <span class="token function">onBlur</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onTouched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token function">writeValue</span><span class="token punctuation">(</span>value<span class="token punctuation">:</span> string<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span><span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!==</span> <span class="token keyword">this</span><span class="token punctuation">.</span>model<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>model <span class="token operator">=</span> value<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// Set the function to be called when the control receives a change event.</span>
    <span class="token function">registerOnChange</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">:</span> any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>onChange <span class="token operator">=</span> fn<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// registerOnTouched(fn: any) : void</span>
    <span class="token function">registerOnTouched</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>onTouched <span class="token operator">=</span> fn<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>在 datetimepick 中继承：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span>CustomInputComponent<span class="token punctuation">,</span> customInputAccessor<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../class/custom-input.class'</span><span class="token punctuation">;</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'date-time-picker'</span><span class="token punctuation">,</span>
  template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`&lt;input type=&quot;text&quot; class=&quot;form-control&quot; [disabled]=&quot;disabled&quot; [(ngModel)]=&quot;value&quot; (blur)=&quot;onBlur()&quot; /&gt;`</span></span><span class="token punctuation">,</span>
  providers<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token function">customInputAccessor</span><span class="token punctuation">(</span>DateTimePickerComponent<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">DateTimePickerComponent</span> <span class="token keyword">extends</span> <span class="token class-name">CustomInputComponent</span> <span class="token keyword">implements</span> <span class="token class-name">AfterViewInit</span> <span class="token punctuation">{</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> accuracy<span class="token punctuation">:</span> string<span class="token punctuation">;</span>  <span class="token comment">// 日期选择的精度，默认分钟，可选 (hour | day)</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> startDate<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 可选最早日期</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> endDate<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 可选最晚日期</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> maxView<span class="token punctuation">:</span> string<span class="token punctuation">;</span> <span class="token comment">// 最大视图</span>
  @<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> disabled<span class="token punctuation">:</span> boolean <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token comment">// 是否禁用</span>

  @<span class="token function">Output</span><span class="token punctuation">(</span><span class="token punctuation">)</span> change <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EventEmitter</span><span class="token operator">&lt;</span>any<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// input的change事件回调</span>

  <span class="token keyword">private</span> el<span class="token punctuation">;</span> <span class="token comment">// 控件元素</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">:</span> ElementRef</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 继承</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>el <span class="token operator">=</span> el<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">ngAfterViewInit</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 原本的内容</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>效果图：</p> <p><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/1495970682%281%29.png" alt="image"></p> <h2 id="结束语"><a href="#结束语" aria-hidden="true" class="header-anchor">#</a> 结束语</h2> <p>这节我们讲了自定义表单相关的一些概念，以及自定义一个时间选择 input 表单的实现过程。<br>
很多时候我们都需要对不同的 input 自行封装，所以也可以单独抽象出来 Class 方便继承，又或者封装成指令等方式都是可以的。<br> <a href="https://github.com/godbasin/godbasin.github.io/tree/blog-codes/angular2-free/5-custom-input-component" target="_blank" rel="noopener noreferrer">此处查看项目代码<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a><br> <a href="http://angular2-free.godbasin.com/5-custom-input-component/index.html#/home/page-setting" target="_blank" rel="noopener noreferrer">此处查看页面效果<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/front-end-others/angular-free/5-custom-input-component.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/front-end-playground/front-end-others/angular-free/4-create-sidebar.html" class="prev">
          4. 制作左侧自动定位菜单
        </a></span> <span class="next"><a href="/front-end-playground/front-end-others/angular-free/6-two-form-building.html">
          6. 模型驱动和模板驱动的表单
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-394" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty0.svg);"></div></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.e5cf4092.js" defer></script><script src="/front-end-playground/assets/js/3.c853767c.js" defer></script><script src="/front-end-playground/assets/js/2.eed49fdd.js" defer></script><script src="/front-end-playground/assets/js/57.7dfe2fbe.js" defer></script>
  </body>
</html>
